#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "prgm.h"

#define UNIT 1
#define NOT_UNIT 3

#define ACC0 0
#define ACC1 1
#define ACC2 2
#define ACC3 3
#define ACC4 4
#define ACC5 5
#define ACC6 6
#define ACC7 7
#define ACC 8

#define PUSH 9
#define PUSHACC0 10
#define PUSHACC1 11
#define PUSHACC2 12
#define PUSHACC3 13
#define PUSHACC4 14
#define PUSHACC5 15
#define PUSHACC6 16
#define PUSHACC7 17
#define PUSHACC 18
#define POP 19

#define ASSIGN 20

#define ENVACC1 21
#define ENVACC2 22
#define ENVACC3 23
#define ENVACC4 24
#define ENVACC 25

#define PUSHENVACC1 26
#define PUSHENVACC2 27
#define PUSHENVACC3 28
#define PUSHENVACC4 29
#define PUSHENVACC 30
#define PUSH_RETADDR 31

#define APPLY 32
#define APPLY1 33
#define APPLY2 34
#define APPLY3 35

#define APPTERM 36
#define APPTERM1 37
#define APPTERM2 38
#define APPTERM3 39

#define RETURN 40
#define RESTART 41
#define GRAB 42

#define CLOSURE 43
#define CLOSUREREC 44
#define OFFSETCLOSUREM2 45
#define OFFSETCLOSURE0 46
#define OFFSETCLOSURE2 47
#define OFFSETCLOSURE 48
#define PUSHOFFSETCLOSUREM2 49
#define PUSHOFFSETCLOSURE0 50
#define PUSHOFFSETCLOSURE2 51
#define PUSHOFFSETCLOSURE 52

#define GETGLOBAL 53
#define PUSHGETGLOBAL 54
#define GETGLOBALFIELD 55
#define PUSHGETGLOBALFIELD 56
#define SETGLOBAL 57

#define ATOM0 58
#define ATOM 59
#define PUSHATOM0 60
#define PUSHATOM 61

#define MAKEBLOCK 62
#define MAKEBLOCK1 63
#define MAKEBLOCK2 64
#define MAKEBLOCK3 65
#define MAKEFLOATBLOCK 66

#define GETFIELD0 67
#define GETFIELD1 68
#define GETFIELD2 69
#define GETFIELD3 70
#define GETFIELD 71
#define GETFLOATFIELD 72
#define SETFIELD0 73
#define SETFIELD1 74
#define SETFIELD2 75
#define SETFIELD3 76
#define SETFIELD 77
#define SETFLOATFIELD 78
#define VECTLENGTH 79
#define GETVECTITEM 80
#define SETVECTITEM 81
#define GETSTRINGCHAR 82
#define SETSTRINGCHAR 83
#define BRANCH 84
#define BRANCHIF 85
#define BRANCHIFNOT 86
#define SWITCH 87

#define BOOLNOT 88
#define PUSHTRAP 89
#define POPTRAP 90
#define RAISE 91
#define CHECK_SIGNALS 92

#define CCALL1 93
#define CCALL2 94
#define CCALL3 95
#define CCALL4 96
#define CCALL5 97
#define CONST0 99
#define CONST1 100
#define CONST2 101
#define CONST3 102
#define CONSTINT 103
#define PUSHCONST0 104
#define PUSHCONST1 105
#define PUSHCONST2 106
#define PUSHCONST3 107
#define PUSHCONSTINT 108

#define NEGINT 109
#define ADDINT 110
#define SUBINT 111
#define MULTINT 112
#define DIVINT 113
#define MODINT 114
#define ANDINT 115
#define ORINT 116
#define XORINT 117
#define LSLINT 118
#define LSRINT 119
#define ASRINT 120
#define EQ 121
#define NEQ 122
#define LTINT 123
#define LEINT 124
#define GTINT 125
#define GEINT 126

#define OFFSETINT 127
#define OFFSETREF 128
#define GETMETHOD 130
#define BEQ 131
#define BNEQ 132
#define BLTINT 133

#define BLEINT 134

#define BGTINT 135
#define BGEINT 136
#define ULTINT 137
#define UGEINT 138
#define BULTINT 139
#define BUGEINT 140
#define GETPUBMETHOD 141
#define GETDYNMET 142
#define STOP 143
#define EVENT 144
#define BREAK 145

#define STACK_SIZE 200

void print_element(int16_t t);
#include "lib.h"

struct section
{
    char * name;
    int32_t size ;
    int32_t offset;
};

struct stack
{
    int16_t data[100];
    int32_t size;
    int32_t offset;
};


int16_t accu = 1;

struct stack sp;
int16_t trap_sp = 0;
int16_t extra_args = 1;
int16_t env = 1;
int16_t *global ;
int32_t start_offset ;

void push_stack( int16_t n)
{
    if(sp.offset >= sp.size)
    {
        printf("sortie de pile push");
        exit(1);
    }
    //printf("push_stack %d \n",n);
    //print_stack();
    sp.data[sp.offset]=n;
    sp.offset ++;
    //printf("after push \n");
    //print_stack();
    //printf("end push stack\n");
}

int16_t pop_stack()
{
    if(sp.offset == 0)
    {
        return sp.data[0];
    }
    sp.offset --;
    return sp.data[sp.offset];


}

void pop_stack_n(int n)
{
    sp.offset -= n;
}

int16_t read_sp()
{
    return sp.data[sp.offset];
}

int16_t read_stack_i(int16_t i)
{

    return sp.data[sp.offset-i-1];

}



struct section sections[7];

int8_t get_opcode( const char code[],int32_t * offset)
{
     unsigned char  opcode =code[start_offset +*offset];
     printf("current offset %d %d \n",(*offset)/4, *offset );
    *offset =*offset +4;

    return (unsigned char)opcode;
}

void check_magic_number( const char code[] , int32_t tailleCode )
{
    unsigned char magic[13] = "Caml1999X008";
    int16_t i=0;
    unsigned char c1;
    c1 = code[tailleCode-1];
    printf("c1 %c\n",c1);
    printf("taille = %d\n",tailleCode);
    for(i =0 ;i<12;i++)
    {
         c1 = code[tailleCode-i-1];
          printf("c1 %c\n",c1);

         if(magic[11-i]!= c1)
        {
            printf("error magic");
            exit(0);
        }
    }
    printf("good magic\n");
}

int32_t read_int32( const char code[] , int32_t offset)
{
    unsigned char tab[4];
    tab[0] = code[offset];
    tab[1] = code[offset+1];
    tab[2] = code[offset+2];
    tab[3] = code[offset+3];
    int32_t i = (int32_t)tab[0] <<24 | (int32_t)tab[1] << 16 | (int32_t)tab[2] << 8 | (int32_t)tab[3];

    return i;
}

int32_t read_int8( const char code[] , int32_t offset)
{
    int8_t i = code[offset];

    return i;
}

int32_t get_section_number( const char code[] , int32_t tailleCode )
{

    unsigned char tab[5];
    tab[3] = code[tailleCode-13];
    tab[2] = code[tailleCode-14];
    tab[1] = code[tailleCode-15];
    tab[0] = code[tailleCode-16];
    int32_t i = (int32_t)tab[0] <<24 | (int32_t)tab[1] << 16 | (int32_t)tab[2] << 8 | (int32_t)tab[3];
    printf("n section%d\n", i);
    return i;
}

unsigned char *get_section_name( const char code[], int32_t offset)
{
    unsigned char*  tab = (unsigned char *) malloc(5 * sizeof(unsigned char));
    tab[0] = code[offset];
    tab[1] = code[offset +1];
    tab[2] = code[offset +2];
    tab[3] = code[offset +3];
    return tab;
}


void init_offsets( const char code[],int32_t offset)
{
    int i = 0;
    int32_t tmp_offset = offset;
    for(i=0;i<7;i++)
    {
        if(sections[i].name != NULL)
        {
            sections[i].offset = tmp_offset - sections[i].size;
            tmp_offset -= sections[i].size;
        }

    }
}


int16_t get_parameter(  const char code[],int32_t * offset)
{
    //printf("current offset %d %d  %d \n" ,*offset,code[*offset+1],code[*offset]);
    int16_t value = (unsigned char)code[start_offset + *offset+1]<<8 | (unsigned char)code[start_offset + *offset];
    printf("value %d \n",value);

    *offset=*offset+4;
    return value;

}

int32_t get_parameter_32( const char code[],int32_t * offset)
{
    unsigned char tab[5];
    tab[0] = code[start_offset + *offset];
    tab[1] = code[start_offset + *offset+1];
    tab[2] = code[start_offset + *offset+2];
    tab[3] = code[start_offset + *offset+3];
    int32_t i = (int32_t)tab[3] <<24 | (int32_t)tab[2] << 16 | (int32_t)tab[1] << 8 | (int32_t)tab[0];
    printf(" param 32 %d %d %d %d %d \n", tab[0], tab[1], tab[2], tab[3],i);
    *offset=*offset+4;
    return i;

}

void print_element(int16_t t)
{

        if((t & 0x0001) == 0)
        {
            if((Field(t,-1)& 0x00FF) == closure_tag)
            {
                printf("closure = %d  code value %d %d size = %d\n",t,((Field(t,0)>>1))/4,get_tag(t),get_size(t));
            }
            else
            {
                printf("address = %d \n",t>>1);
            }

        }
        else
        {
            printf("element = %d \n",t>>1);
        }
}

void print_block(int16_t block)
{
    if(is_block(block))
    {
        printf("tag %d \n",get_tag(block));
        printf("size %d \n",get_size(block));
        printf("code value %d \n",get_code_val(block));
        int i = 1;
        for(i=1; i<=get_size(block);i++)
        {
            print_element(Field(block,i));
        }
    }
}

void print_stack()
{
    printf("-------------\n");
    int i = 0;
    printf("offset = %d\n",sp.offset);
    for(i=sp.offset-1;i>=0;i--)
    {
        printf("%d ",i);
        print_element(sp.data[i]);
    }
    printf("-------------\n");
}


void init_global()
{
  int i = 0;
  global = (int16_t *)malloc(initial_data_size * sizeof(int16_t));
  for(i = 0 ; i < initial_data_size ; i ++){
        global[i] = initial_data[i];

    }
}
void print_global()
{
  int i = 0;
  for(i = 0 ; i < initial_data_size ; i ++){
        printf("%d  = %d \n",global[i],i);
        print_element(global[i]);
    }
}

/* Initialise toutes les donnees relatives aux primitives
- Les infos de la section prim
- Le tableau des primitives
*/

void init_prim()
{
    printf("init prim\n");

    // Gestion du tableau de primitive

    int findex = 0; // Index de la primitive

    bytecode_funs = (c_fun *) malloc(sizeof(c_fun)* primitive_number);

    findex=0;
    int i = 0;

    // Remplir le tableau de primitives

    for(findex=0;findex<primitive_number;findex ++){


        // parcourt la liste des primitives implante par l'interprete et affecte les pointeurs de fonctions

        bytecode_funs[findex] = NULL;
        for(i = 0 ; i < C_FUN_NB ; i ++){

            // Affectation du pointeur de fonction si la primitive est implante ( son nom se trouve dans c_fnames)
            if(strcmp(primitives[findex], c_fnames[i]) == 0){
                printf("found \n");
                bytecode_funs[findex] = c_funs[i];
                break;
            }

        }
    }
}

void c_call(int16_t findex, int16_t arg_nb){
    c_fun f = bytecode_funs[findex];
    if(f!= NULL)
    {
        switch(arg_nb){
            case 1: accu = ((int16_t (*)(int16_t)) f)(accu); break;
            case 2: accu = ((int16_t (*)(int16_t,int16_t)) f)(accu, read_stack_i(0)); break;
            case 3: accu = ((int16_t (*)(int16_t,int16_t,int16_t)) f)(accu, read_stack_i(0), read_stack_i(1)); break;
            case 4: accu = ((int16_t (*)(int16_t,int16_t,int16_t,int16_t)) f)(accu, read_stack_i(0), read_stack_i(1), read_stack_i(2)); break;
            default: fprintf(stderr, "invalid ccall\n"); exit(1);
        }
    }
    else
    {
        printf(" -------------- not implemented function %s ------------- \n" , primitives[findex]);
    }
    pop_stack_n(arg_nb - 1);

}


void interpret( const char code[])
{
    int16_t i;


    // int32_t offset = code_section.offset;
    int32_t offset;

    //start_offset = offset;
    start_offset = 0;
    offset = 0;
    unsigned char next_opcode ;

    int32_t last_offset = bytecode_size;
    int32_t tmp_offset;
    int16_t param,param2,param3,tmp;
    int32_t param32;
    int16_t * block;
    int divisor;
    int val;
    int ofs;
    char* chaine;
    int16_t p, v, z, t, y, mod;
    //printf("run %d %d\n",);
    //unsigned int16_t accu1;
    while( offset < last_offset && offset >= 0)
    {
        next_opcode = get_opcode(code,&offset);
        switch (next_opcode)
        {
            case ACC0:
            {
                printf("ACC0 \n");
                accu = read_stack_i(0);
                print_stack();
                break;
            }
            case ACC1:
            {
                printf("ACC1 \n");
                accu = read_stack_i(1);
                break;
            }
            case ACC2:
            {
                printf("ACC2 \n");
                accu = read_stack_i(2);
                break;
            }
            case ACC3:
            {
                printf("ACC3 \n");
                accu = read_stack_i(3);
                break;
            }
            case ACC4:
            {
                printf("ACC4 \n");
                accu = read_stack_i(4);
                break;
            }
            case ACC5:
            {
                printf("ACC5 \n");
                accu = read_stack_i(5);
                break;
            }
            case ACC6:
            {
                printf("ACC6 \n");
                accu = read_stack_i(6);
                break;
            }
            case ACC7:
            {
                printf("ACC7 \n");
                accu = read_stack_i(7);
                break;
            }
            case ACC:
            {
                printf("ACC \n");
                print_stack();
                param = get_parameter(code,&offset);
                accu = read_stack_i(param);
                print_element(accu);
                break;
            }
            case PUSH:
            {
                printf("PUSH \n");
                push_stack(accu);
                break;
            }
            case PUSHACC0:
            {
                printf("PUSHACC0 \n");
                push_stack(accu);
                accu = read_stack_i(0);
                break;
            }
            case PUSHACC1:
            {
                printf("PUSHACC1 \n");
                push_stack(accu);
                accu = read_stack_i(1);
                break;
            }
            case PUSHACC2:
            {
                printf("PUSHACC2 \n");
                push_stack(accu);
                accu = read_stack_i(2);
                printf("accu = %d",accu);
                break;
            }
            case PUSHACC3:
            {
                printf("PUSHACC3 \n");
                push_stack(accu);
                accu = read_stack_i(3);
                break;
            }
            case PUSHACC4:
            {
                printf("PUSHACC4 \n");
                push_stack(accu);
                accu = read_stack_i(4);
                break;
            }
            case PUSHACC5:
            {
                printf("PUSHACC5 \n");
                push_stack(accu);
                accu = read_stack_i(5);
                break;
            }
            case PUSHACC6:
            {
                printf("PUSHACC6 \n");
                push_stack(accu);
                accu = read_stack_i(6);
                break;
            }
            case PUSHACC7:
            {
                printf("PUSHACC7 \n");
                push_stack(accu);
                accu = read_stack_i(7);
                break;
            }
            case PUSHACC:
            {
                printf("PUSHACC \n");
                push_stack(accu);
                param = get_parameter(code,&offset);
                accu = read_stack_i(param);
                break;
            }
            case POP:
            {
                printf("POP \n");
                param = get_parameter(code,&offset);
                pop_stack_n(param);
                break;
            }
            case ASSIGN:
            {
                printf("ASSIGN \n");
                sp.data[sp.offset - get_parameter(code,&offset) - 1]=accu;
                accu = UNIT;
                break;
            }
             case ENVACC1:
             {
                printf("ENVACC1 \n");
                accu =  Field(env,1);
                break;
             }
             case ENVACC2:
             {
                printf("ENVACC2 \n");
                accu = Field(env,2);
                break;
             }
             case ENVACC3:
             {
                printf("ENVACC3 \n");
                accu = Field(env,3);
                break;
             }
             case ENVACC4:
             {
                printf("ENVACC4 \n");
                accu = Field(env,4);
                break;
             }
            case APPLY:
            {
                printf("APPLY \n");
                param = get_parameter(code,&offset);
                extra_args = param - 1;

                offset = get_code_val(accu);
                env = accu;
                break;
            }
            case APPLY1:
            {
                printf("APPLY1 \n");
                printf("extra args = %d\n",extra_args);
                print_stack();
                param = pop_stack();
                push_stack(make_value(extra_args));
                push_stack(env);
                push_stack(make_value(offset));
                push_stack(param);
                printf("arg = ");
                print_element(param);
                printf("env = ");
                print_element(env);
                extra_args= 0;
                env = accu;
                print_element(accu);
                printf("offset = %d %d %d\n",(get_code_val(accu)-start_offset)/4,get_code_val(accu),start_offset);
                offset = get_code_val(accu);
                print_stack();
                break;
              }
             case APPLY2:
             {
                printf("APPLY2 \n");
                param = pop_stack();
                param2 = pop_stack();
                push_stack(extra_args);
                push_stack(env);
                push_stack(make_value(offset));
                push_stack(param2);
                push_stack(param);
                extra_args= 1;
                env = accu;
                offset = get_code_val(accu);
                break;
             }
            case APPLY3:
            {
                printf("APPLY3 \n");
                param = pop_stack();
                param2 = pop_stack();
                tmp = pop_stack();
                push_stack(extra_args);
                push_stack(env);
                push_stack(make_value(offset));
                push_stack(tmp);
                push_stack(param2);
                push_stack(param);
                extra_args= 2;
                env = accu;
                offset = get_code_val(accu);
                break;
            }
            case ENVACC:
            {
                printf("ENVACC \n");
                param = get_parameter(code,&offset);
                accu = Field(env, param);
                break;
            }
            case APPTERM:
            {
                printf("APPTERM \n");
                print_stack();
                param = get_parameter(code,&offset); // n
                param2 = get_parameter(code,&offset); //s
                for(i = 0 ; i < param ; i ++)
                  sp.data[sp.offset - param2 + i] = sp.data[sp.offset - param + i];
                pop_stack_n(param2 - param);
                extra_args += param - 1;
                offset = get_code_val(accu);
                env = accu;
                print_stack();
                break;
            }
            case APPTERM1:
            {
                printf("APPTERM1 \n");
                //recupere le haut de la pile sp
                int16_t  arg = pop_stack();
                int16_t n = get_parameter(code,&offset)-1; //le nombre d'element a pop
                pop_stack_n(n);
                push_stack(arg);
                offset =  get_code_val(accu);
                env = accu;
                break;
            }
            case APPTERM2:
            {
                printf("APPTERM2 \n");
                print_stack();
                //recupere le haut de la pile sp
                param = pop_stack();
                param2 = pop_stack();
                tmp= get_parameter(code,&offset)-2; //le nombre d'element a pop
                printf("pop %d elements\n",tmp);
                pop_stack_n(tmp);

                push_stack(param2);
                push_stack(param);
                offset = get_code_val(accu);
                print_element(accu);
                env = accu;
                print_stack();
                extra_args += 1;
                printf("extra args = %d",extra_args);
                break;
            }
            case APPTERM3:
            {
                printf("APPTERM3 \n");
                //recupere le haut de la pile sp
                print_stack();
                param = pop_stack();
                param2 = pop_stack();
                param3 = pop_stack();
                tmp = get_parameter(code,&offset)-3; //le nombre d'element a pop
                pop_stack_n(tmp);
                push_stack(param3);
                push_stack(param2);
                push_stack(param);
                offset =  get_code_val(accu);
                env = accu;
                extra_args += 2;
                print_stack();
                break;
            }
            case RETURN:
            {
                param = get_parameter(code,&offset);
                printf("RETURN %d \n",param);
                print_element(accu);
                print_stack();
                pop_stack_n(param);
                if (extra_args > 0) {
                    printf("extra args >0 %d\n",extra_args);
                    extra_args--;
                    offset = get_code_val(accu);
                    env = accu;
                  } else {
                    print_stack();
                    offset = get_value(pop_stack());
                    printf("offset %d \n",(offset-start_offset)/4);
                    env = pop_stack();
                    extra_args = get_value(pop_stack());
                  }
                break;
            }
            case GRAB:
            {
                printf("GRAB \n");
                print_stack();
                param = get_parameter(code,&offset);
                if(extra_args >= param)
                {
                    printf("decrement extra args \n");
                    extra_args -= param;
                }
                else
                {

                    param2 = 1 + extra_args; /* arg1 + extra args */

                    tmp = alloc_block(&block, param2 + 2, closure_tag);
                    accu = tmp;
                    Field(accu, 1) = env;
                    for (i = 0; i < param2; i++) Field(accu, i + 2) = pop_stack();
                    printf("current grab offset %d \n",offset);
                    Field(accu, 0) = make_value(offset - 12); /* Point to the preceding RESTART instr. */
                    print_stack();
                    offset = get_value(pop_stack());
                    print_element(accu);
                    printf("grab param = %d offset = %d %d \n",param2,(offset-start_offset)/4, offset);
                    env = pop_stack();
                    extra_args = get_value(pop_stack());
                }
                break;
            }
            case CLOSURE:
            {
                printf("CLOSURE \n");
                //print_stack();
                param = get_parameter(code,&offset); // n
                param2 = get_parameter(code,&offset)-1; // offset
                if(param>0)
                {
                    printf("PUSH ACCU \n");
                    push_stack(accu);
                }
                    accu = alloc_block(&block,param+1,closure_tag);
                    //printf("addr %d\n",accu);
                    //printf("value %d",(int16_t *)accu);
                    *(get_field(accu,0)) = make_value(offset + param2 *4);
                    Field(accu,0) = make_value(offset + param2 *4);
                    printf("CLOSURE offset = %d \n",((offset + param2 *4)-start_offset)/4);
                    //printf("tmp_addr %d value = %d %d \n",tmp_addr,*(get_field(accu,0)),offset + param2);
                    //get_field(accu,0) = offset + param2;
                    for (i = 0; i < param; i++) Field(accu,i+1) = pop_stack();
                    print_stack();
                break;
            }
            case CLOSUREREC:
            {
                printf("CLOSUREREC \n");
                print_stack();
                param = get_parameter(code,&offset); // f
                param2 = get_parameter(code,&offset); // v

                param3 = get_parameter(code,&offset)-1; // o
                if(param2 >0)
                {
                    push_stack(accu);
                }
                printf("size %d\n",(2*param-1+param2));
                    accu = alloc_block(&block,(2*param-1+param2),closure_tag); // Alloc Closure

                    int16_t * tmp_block = (get_field(accu,param*2-1)); // element 2f-1 de la closure
                    // initialise les v derniers elements de la closure aux v elements poppe de la pile
                    for (i = 0; i < param2; i++) {
                        *tmp_block++ = pop_stack();
                    }
                    // initialisation de la valeur du code
                    tmp_block = (get_field(accu,0));
                    //printf("closurec @ tmp_block %d \n",get_address((get_field(accu,0))));
                    *tmp_block = make_value(offset + (param3 * 4));
                    printf("offset closurerec = %d %d\n", *tmp_block>>1, ((*tmp_block>>1)-start_offset)/4);

                    //accu = block; // Closure dans l'accu

                    printf("accu = %d \n",accu);
                    push_stack(accu);
                    // Les f premiers elements sont initialise avec des block infixe
                    for (i = 1; i < param; i++)
                    {
                        printf("read from t \n");
                        *tmp_block = make_header(i*2,infix_tag); // Allocation d'un bloc infixe
                        tmp_block++;
                        // Valeur du block = parametre du bytecode
                        *tmp_block = make_value(offset +get_parameter(code,&offset)); // t
                        push_stack(*tmp_block); // push l adresse du bloc tmp_block
                        tmp_block++;
                    }
                print_stack();
                break;
            }
            case OFFSETCLOSUREM2:
            {
                printf("OFFSETCLOSUREM2 \n");
                accu = env - 2;
                break;
            }
            case OFFSETCLOSURE0:
            {
                printf("OFFSETCLOSURE0 \n");
                accu = env;
                break;
            }
            case OFFSETCLOSURE2:
            {
                printf("OFFSETCLOSURE2 \n");
                printf("env = %d\n",env);
                accu = env+2;
                break;
            }
            case OFFSETCLOSURE:
            {
                printf("OFFSETCLOSURE \n");
                param = get_parameter(code,&offset);
                accu = env + param*2;
                break;
            }
            case PUSHOFFSETCLOSUREM2:
            {
                printf("PUSHOFFSETCLOSUREM2 \n");
                push_stack(accu);
                accu = env-2;
                break;
            }
            case PUSHOFFSETCLOSURE0:
            {
                printf("PUSHOFFSETCLOSURE0 \n");
                push_stack(accu);
                accu = env;
                print_element(env);
                break;
            }
            case GETGLOBAL:
            {
                printf("GETGLOBAL \n");
                param = get_parameter(code,&offset);
                accu = global[param];
                //sprint_element(accu);
                //print_global();
                break;
            }
            case PUSHGETGLOBAL:
            {
                printf("PUSHGETGLOBAL not finished \n");
                param = get_parameter(code,&offset);
                push_stack(accu);
                accu = global[param];
                break;
            }
            case GETGLOBALFIELD:
            {
                printf("GETGLOBALFIELD not finished\n");
                param = get_parameter(code,&offset);
                param2 = get_parameter(code,&offset);
                accu = global[param];
                accu = Field(accu , param2);
                break;
            }
            case PUSHGETGLOBALFIELD:
            {
                printf("PUSHGETGLOBALFIELD not finished \n");
                push_stack(accu);
                param = get_parameter(code,&offset);
                param2 = get_parameter(code,&offset);
                accu = global[param];
                printf("global field %d = %d", param , accu);
                accu = Field(accu , param2);
                break;
            }
            case SETGLOBAL:
            {
                printf("SETGLOBAL not finished\n");
                param = get_parameter(code,&offset);
                global[param] = accu;
                printf("set global = %d \n", global[param]);
                accu = UNIT;
                //accu = Field(global,param);
                //accu = Field(accu , param2);
                break;
            }
            case ATOM0:
            {
                printf("ATOM0 \n");
                accu = atom[0];
                break;
            }
            case ATOM:
            {
                printf("ATOM \n");
                param = get_parameter(code,&offset); // n
                accu = atom[param];
                break;
            }
            case PUSHATOM0:
            {
                printf("PUSHATOM \n");
                push_stack(accu);
                param = get_parameter(code,&offset); // n
                accu = atom[param];
                break;
            }
            case MAKEBLOCK:
            {
                printf("MAKEBLOCK \n");
                param = get_parameter(code,&offset); // n
                param2 = get_parameter(code,&offset); //tag
                tmp = alloc_block(&block, param, param2);
                Field(tmp, 0) = accu;
                for (i = 1; i < param; i++) Field(tmp, i) = pop_stack();
                accu = tmp;
                break;
            }
            case MAKEBLOCK1:
            {
                printf("MAKEBLOCK1 \n");
                param = get_parameter(code,&offset);
                tmp = alloc_block(&block, 1, param);
                Field(tmp, 0) = accu;
                accu = tmp;
                break;
            }
            case MAKEBLOCK2:
            {
                printf("MAKEBLOCK2 \n");
                param = get_parameter(code,&offset);
                tmp = alloc_block(&block, 2, param);
                Field(tmp, 0) = accu;
                Field(tmp, 1) = pop_stack();
                accu = tmp;
                break;
            }
            case MAKEBLOCK3:
            {
                printf("MAKEBLOCK3 \n");
                param = get_parameter(code,&offset);
                tmp = alloc_block(&block, 3, param);
                Field(tmp, 0) = accu;
                Field(tmp, 1) = pop_stack();
                Field(tmp, 2) = pop_stack();
                accu = tmp;
                break;
            }
            case MAKEFLOATBLOCK:
            {
                printf("MAKEFLOATBLOCK\n");
                param = get_parameter(code,&offset);
                tmp = alloc_block(&block, param * 2, double_array_tag);
                store_double_field(block, 0, accu);
                for (i = 1; i < param; i++)
                {
                    store_double_field(block, i, (double) pop_stack());
                }
                accu = tmp;
                break;
            }
            case GETFIELD0:
            {
                printf("GETFIELD 0 \n");

                accu = Field(accu, 0);
                break;
            }
            case GETFIELD1:
            {
                print_stack();
                print_element(accu);
                printf("GETFIELD 1 \n");
                accu = Field(accu, 1);
                break;
            }
            case GETFIELD2:
            {
                printf("GETFIELD 2 \n");
                accu = Field(accu, 2);
                break;
            }
            case GETFIELD3:
            {
                printf("GETFIELD 3 \n");
                accu = Field(accu, 3);
                break;
            }
             case GETFIELD:
             {
                printf("GETFIELD  \n");
                accu = Field(accu, pop_stack());
                break;
             }
            case GETFLOATFIELD:
            {
                printf("GETFLOATFIELD \n");
                param = get_parameter(code,&offset);
                accu = Field(accu,param);
                break;
            }
            case SETFIELD0:
            {
                printf("SETFIELD0 \n");
                Field(accu,0) = pop_stack();
                accu = UNIT;
                break;
            }
            case SETFIELD1:
            {
                printf("SETFIELD1 \n");
                Field(accu,1) = pop_stack();
                accu = UNIT;
                break;
            }
            case SETFIELD2:
            {
                printf("SETFIELD2 \n");
                Field(accu,2) = pop_stack();
                accu = UNIT;
                break;
            }
            case SETFIELD3:
            {
                printf("SETFIELD3 \n");
                Field(accu,3) = pop_stack();
                accu = UNIT;
                break;
            }
            case SETFIELD:
            {
                printf("SETFIELD \n");
                param = get_parameter(code,&offset);
                Field(accu,param) = pop_stack();
                accu = UNIT;
                break;
            }
            case SETFLOATFIELD:
            {
                printf("SETFLOATFIELD \n");
                param = get_parameter(code,&offset);
                Field(accu,param) = pop_stack();
                accu = UNIT;
                break;
            }
            case VECTLENGTH:
            {
                printf("VECTLENGTH \n");
                accu = get_size(accu);
                 break;
            }
            case GETVECTITEM:
            {
                printf("GETVECTITEM \n");
                param = get_parameter(code,&offset);
                accu = Field(accu,param);
                break;
            }
            case SETVECTITEM:
            {
                printf("SETVECTITEM \n");
                param = pop_stack();
                param2 = pop_stack();
                Field(accu,param) = param;
                accu = UNIT;
               break;
            }
            case GETSTRINGCHAR:
            {
                printf("GETSTRINGCHAR \n");
                param = get_parameter(code,&offset);
                //accu = get_char(accu,param);
                break;
            }
            case SETSTRINGCHAR:
            {
                printf("SETSTRINGCHAR \n");
                param = pop_stack();
                param2 = pop_stack();
                get_char(accu,param) = param2;
                accu = UNIT;
                break;
            }
            case BRANCH:
            {
                i = get_parameter(code,&offset)+1;
                printf("BRANCH   %d\n",i);
                offset+= i*4-8;
                break;
            }
            case BRANCHIF:
            {
                i = get_parameter(code,&offset);
                printf("BRANCHIF   %d\n",i) + 2;
                if(accu != UNIT)
                    offset+= i *4;
                break;
            }
            case BRANCHIFNOT:
            {
                printf("BRANCHIFNOT \n");
                i = get_parameter(code,&offset) ;
                printf("BRANCHIFNOT   %d\n",i);
                if(accu ==  UNIT)
                    offset+= i *4;
                break;
            }
            case SWITCH:
            {
                printf("SWITCH\n");
                print_stack();
                param32 = get_parameter_32(code,&offset); // n (sizeTag << 16)+ sizeLong
                tmp_offset = offset;/*
                for(i = 0;i< param+param2;i++)
                {
                    param3 = get_parameter(code,&offset);
                    printf("%d\n",param3);
                }*/
                if(is_block(accu))
                {
                    printf("switch block %d %d %d\n",(int32_t)(param32) ,  (param32 >>16 ), (int16_t) param32);
                    tmp = get_tag(accu);
                    int32_t index = (param32 & 0xFFFF) + tmp; // tag + sizelong
                    offset += index*4;

                    param3 = get_parameter(code,&offset);
                    printf("tmp = %d, ofs = %d\n", tmp, param32 & 0xFFFF);
                    offset = tmp_offset;
                    offset += param3*4;
                }
                else
                {
                    printf("switch value %d\n",get_value(accu)*4);
                    offset += get_value(accu)*4;
                    param3 = get_parameter(code,&offset);
                    offset = tmp_offset ;
                    printf("offset before %d %d %d %d\n",offset,( offset-start_offset)/4 ,get_value(accu),param3);

                   offset += param3*4;
                   printf("offset before %d %d \n",offset,( offset-start_offset)/4);
                }
                break;
            }
            case BOOLNOT:
            {
                printf("BOOLNOT \n");
                accu = make_value(~get_value(accu));
                break;
            }
            case PUSHTRAP:
            {
                printf("PUSHTRAP \n");
                ofs = get_parameter(code,&offset);
                push_stack(extra_args);
                push_stack(env);
                push_stack(trap_sp);
                push_stack(offset + ofs * 4);
                trap_sp = make_value(offset);
                break;
            }
            case POPTRAP:
            {
                printf("POPTRAP \n");
                y = pop_stack();
                trap_sp = pop_stack();
                z = pop_stack();
                t = pop_stack();
                break;
            }
            case RAISE:
            {
                printf("RAISE \n");
                if (sp.data == NULL){
                  printf("Error raise exception \n");
                  break;
                }else{
                  //sp = trap_sp;
                  offset = pop_stack();
                  trap_sp = pop_stack();
                  env = pop_stack();
                  extra_args = pop_stack();
                }
                break;
            }
            case CHECK_SIGNALS:
            {
                printf("CHECK_SIGNALS \n");
                //if(signals) handles
                break;
            }
            case CCALL1:
            {
                param = get_parameter(code,&offset);
                printf("CCALL1 %d \n",param);
                c_call(param,1);
                break;
            }
            case CCALL2:
            {
                print_stack();
                param = get_parameter(code,&offset);
                printf("CCALL2 %d \n",param);
                c_call(param,2);
                print_stack();
                break;
            }
            case CCALL3:
            {
                param = get_parameter(code,&offset);
                printf("CCALL3 %d \n",param);
                c_call(param,3);
                break;
            }
            case CCALL4:
            {
                print_stack();
                param = get_parameter(code,&offset);
                printf("CCALL4 %d \n",param);
                c_call(param,4);
                break;
            }
            case CCALL5:
            {
                param = get_parameter(code,&offset);
                printf("CCALL5 %d \n",param);
                c_call(param,5);
                break;
            }
            case CONST0:
            {
                printf("CONST0 \n");
                accu = make_value(0);
                break;
            }
            case CONST1:
            {
                printf("CONST1 \n");
                accu = make_value(1);
                break;
            }
            case CONST2:
            {
                printf("CONST2 \n");
                accu = make_value(2);
                break;
            }
            case CONST3:
            {
                printf("CONST3 \n");
                accu = make_value(3);
                break;
            }
            case CONSTINT:
            {
                printf("CONSTINT \n");
                param = get_parameter(code,&offset);

                accu = make_value(param);
                break;
            }
            case PUSHENVACC1:
            {
                printf("PUSHENVACC1 \n");
                push_stack(accu);
                accu = Field(env,1);
                break;
            }
            case PUSHENVACC2:
            {
                printf("PUSHENVACC2 %d\n",accu);
                push_stack(accu);
                accu = Field(env,2);
                print_element(accu);
                break;
            }
            case PUSHENVACC3:
            {
                printf("PUSHENVACC3 %d\n",accu);
                push_stack(accu);
                accu = Field(env,3);
                printf("accu %d \n",accu);
                print_element(accu);
                break;
            }
            case PUSHENVACC4:
            {
                printf("PUSHENVACC4 \n");
                push_stack(accu);
                accu = Field(env,4);
                break;
            }
            case PUSHCONST0:
            {
                printf("PUSHCONST0 \n");
                push_stack(accu);
                accu = make_value(0);
                break;
            }
            case PUSHCONST1:
            {
                printf("PUSHCONST1 \n");
                push_stack(accu);
                accu = make_value(1);
                break;
            }
            case PUSHCONST2:
            {
                printf("PUSHCONST2 \n");
                push_stack(accu);
                accu = make_value(2);
                break;
            }
            case PUSHCONST3:
            {
                printf("PUSHCONST3 \n");
                push_stack(accu);
                accu = make_value(3);
                break;
            }
            case PUSHCONSTINT:
            {
                printf("PUSHCONSTINT \n");
                param = get_parameter(code,&offset);
                push_stack(accu);
                accu =make_value(param);
                break;
            }
            case NEGINT:
            {
                printf("NEGINT \n");
                accu = make_value(-get_value(accu));
                break;
            }
            case ADDINT:
            {
                printf("ADDINT \n");
                accu = make_value(get_value(pop_stack()) + get_value(accu)) ;
                break;
            }
            case SUBINT:
            {
                printf("SUBINT \n");
                accu = make_value(get_value(accu) - get_value(pop_stack()));
                break;
            }
            case MULTINT:
            {
                print_stack();
                printf("MULTINT");
                param = pop_stack();
                printf(" %d %d\n",get_value(accu),get_value(param));
                accu = make_value(get_value(accu)* get_value(param));
                break;
            }
            case DIVINT:
            {
                printf("DIVINT \n");
                divisor = pop_stack();
                if (divisor == 0)

                {
                    //Setup_for_c_call; caml_raise_zero_divide();
                    printf("division by 0\n");
                    accu = make_value(0);
                }
                else
                {
                    accu = make_value(get_value(accu) / get_value(divisor));
                }
                break;
            }
            case MODINT:
            {
                mod = pop_stack();
                printf("MODINT %d %d\n",get_value(mod),get_value(accu));
                if (mod == 0){
                  //Setup_for_c_call; caml_raise_zero_divide();
                  printf("error division by zero \n");
                }
                accu = make_value(get_value(accu)%get_value(mod));
                printf("accu = %d\n",get_value(accu));
                print_stack();
                  break;
            }
            case ANDINT:
            {
                printf("ANDINT \n");
                accu = accu & pop_stack();
                break;
            }
            case ORINT:
            {
                printf("ORINT \n");
                accu = accu | pop_stack();
                break;
            }
            case XORINT:
            {
                printf("XORINT \n");
                accu = make_value(get_value(accu) ^ get_value(pop_stack()));
                break;
            }
            case LSLINT:
            {
                printf("LSLINT \n");
                accu = make_value(get_value(accu) << pop_stack());
                break;
            }
            case LSRINT:
            {
                printf("LSRINT \n");
                accu = make_value(get_value(accu) >> get_value(pop_stack()));
                break;
            }
            case ASRINT:
            {
                printf("ASRINT \n");
                accu =  make_value(get_value(accu) >> get_value(pop_stack()));
                break;
            }
            case EQ:
            {
                printf("EQ \n");
                //val = get_parameter(code,&offset);
                val = pop_stack();
                if( accu == val )
                    accu = NOT_UNIT;
                else
                    accu = UNIT;
                break;
            }
            case NEQ:
            {
                printf("NEQ \n");
                val = pop_stack();
                printf("NEQ  %d %d\n",get_value(accu) , get_value(val));
                if(accu != val)
                  accu = NOT_UNIT;
                else
                  accu = UNIT;
                break;
            }
            case LTINT:
            {
                printf("LTINT \n");
                val = pop_stack();
                if(accu < val)
                    accu = NOT_UNIT;
                else
                    accu = UNIT;
                break;
            }
            case LEINT:
            {
                printf("LEINT \n");
                val = pop_stack();
                if(accu <= val)
                  accu = NOT_UNIT;
                else
                  accu = UNIT;
                break;
                  case GTINT:
                printf("GTINT \n");
                val = pop_stack();
                if (accu > val)
                  accu = 3;
                else
                  accu = UNIT;
                break;
            }
            case GEINT:
            {
                printf("GEINT \n");
                val = pop_stack();
                if (accu >= val)
                  accu = NOT_UNIT;
                else
                  accu = UNIT;
                break;
            }
            case OFFSETINT:
            {
                printf("OFFSETINT \n");
                print_stack();
                param = get_parameter(code,&offset);
                printf("before %X  %X %X\n",get_value(accu),param , make_value(param));
                accu += param <<1;
                printf("after %d  %X %d\n",get_value(accu),accu , make_value(param));
                break;
            }
            case OFFSETREF:
            {
                printf("OFFSETREF\n");
                param = get_parameter(code,&offset);
                Field(accu, 0) +=  param << 1;
                accu = UNIT;
                break;
            }
            case GETMETHOD:
            {
                printf("GETMETHOD \n");
                tmp = accu;
                accu = Field(read_sp(),0);
                accu = Field(accu,tmp);
                break;
            }
            case BEQ:
            {
                printf("BEQ \n");
                val = get_parameter(code,&offset);
                ofs = get_parameter(code,&offset) - 1;
                if( accu == val)
                {
                    offset += ofs-1;
                }
                break;
            }
            case BNEQ:
            {
                printf("BNEQ \n");
                val = get_parameter(code,&offset);
                ofs = get_parameter(code,&offset) - 1;
                if( accu != val)
                {
                    offset += ofs * 4 ;
                }
                break;
            }
            case BLTINT:
            {
                printf("BLTINT \n");
                val = get_parameter(code,&offset);
                ofs = get_parameter(code,&offset);
                if( val < accu )
                {
                    offset += ofs;
                }
                break;
            }
            case BLEINT:
            {
                printf("BLEINT \n");
                val = get_parameter(code,&offset);
                ofs = get_parameter(code,&offset)-1;
                printf("BLEINT to %d %d \n ",((offset+ofs*4)-start_offset)/4,get_value(accu));

                if( val <= get_value(accu) )
                {
                    offset += ofs * 4;
                }
                break;
            }
            case BGTINT:
            {
                printf("BGTINT \n");
                val = get_parameter(code,&offset);
                ofs = get_parameter(code,&offset)-1;
                if( val > get_value(accu) )
                {
                    offset += ofs * 4;
                }
                break;
            }
            case BGEINT:
            {
                  printf("BGEINT \n");
                val = get_parameter(code,&offset);
                ofs = get_parameter(code,&offset)-1;
                if( val >= get_value(accu) )
                {
                    offset += ofs * 4;
                }
                break;
            }
            case BULTINT:
            {
                printf("BULTINT \n");
                val = get_parameter(code,&offset);
                ofs = get_parameter(code,&offset)-1;
                if( val < (uint16_t)(get_value(accu)) )
                {
                    offset += ofs*4;
                }
                break;
            }
            case ULTINT:
            {
                printf("ULTINT \n");
                param = get_parameter(code,&offset);
                if( (uint16_t)get_value(accu) < (uint16_t)param)
                {
                    accu = 3;
                }
                else
                {
                    accu = UNIT;
                }
                break;
            }
            case UGEINT:
            {
                printf("UGEINT \n");
                param = get_parameter(code,&offset);
                if( (uint16_t)get_value(accu) >= (uint16_t)param)
                {
                    accu = 3;
                }
                else
                {
                    accu = UNIT;
                }
                break;
            }
            case BUGEINT:
            {
                printf("BUGEINT \n");
                 val = get_parameter(code,&offset);
                 ofs = get_parameter(code,&offset)-1;
                if( val >= (uint16_t)(get_value(accu)) )
                {
                    offset += ofs*4;
                }
                break;
            }
            case GETPUBMETHOD:
            {
                tmp = Field (accu, 0); // tmp = methode
                push_stack(accu);
                accu = tmp;
                break;
            }
            case GETDYNMET:
            {
                printf("GETDYNLET \n");
                int16_t meths = Field(read_sp(), 0);
              int li = 3, hi = Field(meths,0), mi;
              while (li < hi) {
                mi = ((li+hi) >> 1) | 1;
                if (accu < Field(meths,mi)) hi = mi-2;
                else li = mi;
              }
              accu = Field (meths, li-1);
              break;
            }
            case STOP:
            {
                printf("STOP accu = %d %d\n",get_value(accu),last_offset);
                break;
            }
            case EVENT:
            {
                printf("EVENT \n");
                break;
            }
            case PUSH_RETADDR:
            {
                printf("PUSH_RETADDR\n");
                ofs = get_parameter(code,&offset);
                push_stack(make_value(extra_args));
                push_stack(env);
                push_stack(make_value(offset+(ofs-1)*4));
                break;
            }
            case RESTART:
            {
                printf("RESTART \n");
                //n = *env.size - 2;
                print_element(env);
                print_stack();
                param = get_size(env)- 2;
                printf("param %d \n",param);
                int i;
                for(i=0; i < param; i++){
                    push_stack(Field(env,i+2));
                }
                env = Field(env,1);
                extra_args += param;
                break;
            }
            default:
            {
                printf("============\n unknown opcode %d\n ============= \n",next_opcode);
                break;
            }
        }
    }


    /*
    for(i=code.offset; i<code.offset+code.size;i++)
    {

    }*/
}

void print_sections_info()
{
     int i = 0;
    for(i=0;i<7;i++)
    {
        if(sections[i].name != NULL)
        {
            printf("section name : %s\n",sections[i].name);
            printf("size : %d\n",sections[i].size);
            printf("offset : %d\n", sections[i].offset);
        }

    }
}

void init_stack()
{
    sp.size = STACK_SIZE;
    int i = 0 ;
    for(i=0;i<sp.size;i++)
    {
        sp.data[i] = UNIT;
    }
    sp.offset = 0;
     sp.size = STACK_SIZE;
}

void run()
{
    init_stack();
    /*
    check_magic_number(code,tailleCode);
    int32_t section_number = get_section_number(code,tailleCode);
    int i = 0;
    int offset = 16 + 8 ;
    char * section_name;
    for (i=0;i<section_number;i++)
    {
        section_name = get_section_name(code,tailleCode-offset);
        //printf("section name %s\n",section_name);
        sections[i].name = section_name;
        sections[i].size = read_int32(code,tailleCode-offset+4);
        offset += 8;
    }
    init_offsets(code,tailleCode-offset+8);
    print_sections_info();
    init_prim(code);
    printf(" size %d \n",sp.size);*/
    init_prim();
    interpret(bytecode);
    /*
    int16_t pc = 6;
    while(pc < tailleCode)
    {

        pc++;
    }
    */

}




int main(int argc, char** argv){
  if (argc>5) printf("Il manque un argument.\n");
  else {
    printf("start \n");
    init_heap();
    printf("start \n");
    init_global();
    //FILE *f;
    //f = fopen("test", "rb");
    /*
    fpos_t pos;
    fgetpos (f, &pos);
    fseek(f, 0L, SEEK_END);
    long sz = ftell(f);
    fsetpos(f,&pos);
    int32_t i = 0;

     unsigned char * code = malloc(sizeof(unsigned char)* sz);
    //int16_t* mot = malloc(sizeof(int16_t));
    unsigned char c1;/*
    while(1){
      //printf("a");

      tab[0] = fgetc(f);
      c1 = tab[1];
      if(tab[0] == -1) break;
      tab[1] = fgetc(f);
      c2 = tab[1];
      printf("chars : %c %c\n",c1,c2);
      printf("hex chars : %X %X\n",c1,c2);
      //printf("b");
      mot = (int16_t*)tab;
      //printf("c");
      //if(taille <= 2)
        //printf("mot lu : %X : %X\n",*mot , *mot);
      //printf("d");
      //if (taille < 10)
        printf("%d   0x%X\n",taille, *mot);
      code[taille -1] = *mot;
      code = realloc(code, ++taille * sizeof(int16_t));
      //printf("e");
      //printf("f\n");google code
    }*//*
    do {
        c1= fgetc(f) ;
        code[i] = c1;


        i++;

    } while (i<sz);*/
                //} while (c1 != EOF);
                /*
    printf("size %d\n",(int)sz);
    printf("dernier char %c \n",code[i]);
    fclose(f);
    printf("debut run ...%d\n",i);*/
    printf("start \n");
    run();
    printf("%x %x %x",-1 ,-2,(-1<<1));
    print_stack();
  }
  return 0;
}

